package com.yin.context;

import com.yin.annotation.YinAutowired;
import com.yin.annotation.YinController;
import com.yin.annotation.YinService;
import com.yin.beans.BeanDefinition;
import com.yin.beans.BeanPostProcessor;
import com.yin.beans.BeanWrapper;
import com.yin.context.support.BeanDefinitionReader;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class YinApplicationContext implements BeanFactory{
    private String[] configLocations;
    private BeanDefinitionReader reader;
    private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private Map<String,Object> beanCacheMap = new HashMap<>();
    private Map<String,BeanWrapper> beanWrapperMap = new ConcurrentHashMap<>();
    public YinApplicationContext(String... configLocations){
        this.configLocations = configLocations;
        refresh();
    }
    public void refresh(){
        //定位，加载，注册，依赖注入（lazy-init:false)
        this.reader = new BeanDefinitionReader(configLocations);
        List<String> beanDefinitions = reader.loadBeanDefinitions();
        this.doRegisty(beanDefinitions);
        this.doAutowired();
        System.err.println("end");
    }

    private void doRegisty(List<String> beanDefinitions){

        try {
            for (String className : beanDefinitions){
                //TODO 自定义暂不考虑
                Class clazz = Class.forName(className);
                if(clazz.isInterface()) continue;

                BeanDefinition beanDefinition = reader.registerBean(className);
                if(beanDefinition != null){
                    this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
                }

                Class[] interfaces = clazz.getInterfaces();
                for (Class i : interfaces){
                    this.beanDefinitionMap.put(i.getName(),beanDefinition);
                }


            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }
    @Override
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        String className = beanDefinition.getBeanClassName();
        try {

            BeanPostProcessor beanPostProcessor = new BeanPostProcessor();
            Object instance = instantionBean(beanDefinition); //保证单例
            if(null == instance) return null;
            beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
            BeanWrapper beanWrapper = new BeanWrapper(instance);
            this.beanWrapperMap.put(beanName,beanWrapper);

            beanPostProcessor.postProcessAfterInitialization(instance,beanName);
           // this.populateBen(beanName,instance);
            return this.beanWrapperMap.get(beanName).getWrapperInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private Object instantionBean(BeanDefinition beanDefinition){
        String beanClassName = beanDefinition.getBeanClassName();
        try {

            if(this.beanCacheMap.containsKey(beanClassName)){
                return this.beanCacheMap.get(beanClassName);
            }else {
                Class clazz = Class.forName(beanClassName);
                Object instance = clazz.newInstance();
                this.beanCacheMap.put(beanClassName,instance);
                return instance;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
       return null;
    }
    private void doAutowired(){
        for (Map.Entry<String,BeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()){
            String beanName = beanDefinitionEntry.getKey();
            if(!beanDefinitionEntry.getValue().isLazyInit()){
                this.getBean(beanName);
            }
        }
        for (Map.Entry<String,BeanWrapper> beanWrapperEntry : this.beanWrapperMap.entrySet()){
           populateBen(beanWrapperEntry.getKey(),beanWrapperEntry.getValue().getWrapperInstance());
        }
    }

    public void populateBen(String beanName, Object instance){
        Class clazz = instance.getClass();
        if(!clazz.isAnnotationPresent(YinController.class) ||
                clazz.isAnnotationPresent(YinService.class)){
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields){
            if(!field.isAnnotationPresent(YinAutowired.class)) return;
            YinAutowired autowired = field.getAnnotation(YinAutowired.class);
            String autowiredName = autowired.value().trim();
            if("".equals(autowiredName)){
                autowiredName = field.getType().getName();
            }
            field.setAccessible(true);
            try {
               field.set(instance,this.beanWrapperMap.get(autowiredName).getWrapperInstance()) ;
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    public String[] getBeanDefinitionNames(){
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public int getBeanDefinitionCount(){
        return this.beanDefinitionMap.size();
    }

    public Properties getConfig(){
        return this.reader.getConfig();
    }

    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<>();
        map.put("first",1);
        map.put("second",2);
        map.put("third",3);
        System.err.println(Arrays.toString(map.entrySet().toArray()));
        System.err.println(Arrays.toString(map.keySet().toArray(new String[3])));
        System.err.println(Arrays.toString(map.keySet().toArray()));

    }
}
